using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using Castle.Core;
using Castle.Core.Configuration;
using Castle.Core.Interceptor;
using Castle.MicroKernel.Facilities;

namespace Castle.Facilities.PolicyInjection
{
    public class Rule
    {
        private Type _targetType;

        public Type TargetType
        {
            get
            {
                return _targetType;
            }
        }

        private IRule _instance;

        public IRule Instance
        {
            get
            {
                return _instance;
            }
        }

        private IList<Handler> _handlerCollection;

        public IList<Handler> HandlerCollection
        {
            get
            {
                return _handlerCollection;
            }
        }

        private IDictionary<string, string> _configurationAttributeCollection;

        public IDictionary<string, string> CustomConfigurationAttributeCollection
        {
            get
            {
                return _configurationAttributeCollection;
            }
        }

        public Rule(IConfiguration configuration, PolicyFacility facility, string componentId, ComponentModel componentModel)
        {
            InspectTagElement(configuration);
            InspectRuleConfigurationAttribute(configuration);
            CreateHandlerCollection(configuration);
            AddRuleInstance();
            VerifyRuleMatching(componentId, componentModel);
        }                    

        private void InspectTagElement(IConfiguration configuration)
        {
            if (configuration.Name != "rule")
            {
                throw new InvalidOperationException("Policy rule element must be <rule>");
            }
            if (string.IsNullOrEmpty(configuration.Attributes["type"]) == true)
            {
                throw new InvalidOperationException("Policy rule element must have a \"type\" attribute");
            }
            string targetType = configuration.Attributes["type"];
            _targetType = Type.GetType(targetType, false);
            if (_targetType == null)
            {
                throw new InvalidOperationException("Policy rule type " + targetType + " was not found");
            }
            if (configuration.Children.Count == 0)
            {
                throw new InvalidOperationException("Policy rule element must have at least one child element <handler>");
            }
        }

        private void InspectRuleConfigurationAttribute(IConfiguration configuration)
        {
            try
            {
                object[] ruleConfigurationAttributeCollection =
                    _targetType.GetCustomAttributes(typeof(RuleConfigurationAttribute), false);
                if (ruleConfigurationAttributeCollection.Length != 1)
                {
                    throw new InvalidOperationException("Policy rule " + _targetType.FullName + "does not declare " +
                       "correctly a " + typeof(RuleConfigurationAttribute).FullName + " attribute");
                }
                RuleConfigurationAttribute ruleConfigurationAttribute = ruleConfigurationAttributeCollection[0] as
                    RuleConfigurationAttribute;
                if (ruleConfigurationAttribute == null)
                {
                    throw new InvalidOperationException("Policy rule " + _targetType.FullName + " does not declare " +
                       "correctly a " + typeof(RuleConfigurationAttribute).FullName + " attribute");
                }
                _configurationAttributeCollection = new Dictionary<string, string>();
                foreach (string configurationAttributeName in
                    ruleConfigurationAttribute.ConfigurationAttributeNameCollection)
                {
                    if (string.IsNullOrEmpty(configuration.Attributes[configurationAttributeName]) == true)
                    {
                        throw new InvalidOperationException("Policy rule " + _targetType.FullName + " must have a " +
                            "\"" + configurationAttributeName + "\" attribute");
                    }
                    string configurationAttributeValue = configuration.Attributes[configurationAttributeName];
                    _configurationAttributeCollection.Add(configurationAttributeName, configurationAttributeValue);
                }

            }
            catch (InvalidOperationException ex)
            {
                throw ex;
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException("Policy rule " + _targetType.FullName + " does not declare " +
                       "correctly a " + typeof(RuleConfigurationAttribute).FullName + " attribute", ex);
            }
        }

        private void CreateHandlerCollection(IConfiguration configuration)
        {
            _handlerCollection = new List<Handler>();
            foreach (IConfiguration handlerConfiguration in configuration.Children)
            {
                Handler handler = new Handler(handlerConfiguration);
                _handlerCollection.Add(handler);
            }
        }

        private void AddRuleInstance()
        {
            //if (PolicyFacility.RuleInstanceCollection.ContainsKey(_targetType) == false)
            //{
            //    try
            //    {
            //        IRule rule = Activator.CreateInstance(_targetType) as
            //            IRule;
            //        if (rule == null)
            //        {
            //            throw new InvalidOperationException("Policy rule " + _targetType.FullName + " null " +
            //                "intantiation as " + typeof(IRule).FullName);
            //        }
            //        PolicyFacility.RuleInstanceCollection.Add(_targetType, rule);
            //    }
            //    catch (Exception ex)
            //    {
            //        throw new InvalidOperationException("Cannot instantiate correctly policy rule " +
            //            _targetType.FullName, ex);
            //    }
            //}
            try
            {
                _instance = Activator.CreateInstance(_targetType) as IRule;
                if (_instance == null)
                {
                    throw new InvalidOperationException("Policy rule " + _targetType.FullName + " null " +
                        "intantiation as " + typeof(IRule).FullName);
                }
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException("Cannot instantiate correctly policy rule " +
                    _targetType.FullName, ex);
            }            
        }

        private void VerifyRuleMatching(string componentId, ComponentModel componentModel)
        {
            bool isServiceMatch = _instance.IsMatch(componentModel.Service, _configurationAttributeCollection);
            bool isImplementationMatch = _instance.IsMatch(componentModel.Implementation, _configurationAttributeCollection);
            if ((isServiceMatch == false) && (isImplementationMatch == false))
            {
                throw new InvalidOperationException("Policy rule " + _targetType.FullName + " does not match any " +
                    "method on component " + componentId);
            }
        }   
    }
}
